{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "import uuid\n",
    "import logging\n",
    "import panel as pn\n",
    "\n",
    "pn.extension('terminal')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``Terminal`` provides a way to display outputs or logs from running processes as well as an interactive terminal based on for example Bash, Python or IPython. The Terminal is based on [Xterm.js](https://xtermjs.org/) which enables\n",
    "\n",
    "- Terminal apps that just work: Xterm.js works with most terminal apps such as bash, vim and tmux, this includes support for curses-based apps and mouse event support\n",
    "- Performance: Xterm.js is really fast, it even includes a GPU-accelerated renderer\n",
    "- Rich unicode support: Supports CJK, emojis and IMEs\n",
    "\n",
    "[![Xterm.js](https://raw.githubusercontent.com/xtermjs/xterm.js/master/logo-full.png)](https://xtermjs.org/)\n",
    "\n",
    "### Terminal Widget\n",
    "\n",
    "#### Parameters:\n",
    "\n",
    "For details on other options for customizing the component see the [layout](../../how_to/layout/index.md) and [styling](../../how_to/styling/index.md) how-to guides.\n",
    "\n",
    "- **``clear``** (action): Clears the Terminal.\n",
    "- **``options``** (dict) Initial Options for the Terminal Constructor. cf. [iterminaloptions](https://xtermjs.org/docs/api/terminal/interfaces/iterminaloptions/)\n",
    "- **``output``** (str): System *output* written to the Terminal.\n",
    "- **``value``** (str): User *input* received from the Terminal.\n",
    "- **``write_to_console``** (boolean): If True output is additionally written to the server console. Default value is False.\n",
    "\n",
    "#### Methods\n",
    "\n",
    "* **``write``**: Writes the specified string object to the Terminal.\n",
    "\n",
    "### Terminal Subprocess\n",
    "\n",
    "The `Terminal.subprocess` property makes it easy for you to run subprocesses like `ls`, `ls -l`, `bash`, `python` and `ipython` in the terminal. \n",
    "\n",
    "#### Parameters\n",
    "\n",
    "- **``args``** (str, list):  The arguments used to run the subprocess. This may be a string or a list. The string cannot contain spaces. See [subprocess.run](https://docs.python.org/3/library/subprocess.html) for more details.\n",
    "- **``kwargs``** (dict): Any other arguments to run the subprocess. See [subprocess.run](https://docs.python.org/3/library/subprocess.html) for more details.\n",
    "- **``running``** (boolean, readonly): Whether or not the subprocess is running. Defaults to False.\n",
    "- **``run``** (action): Executes `subprocess.run` in a child process using the args and kwargs parameters provided as arguments or as parameter values on the instance. The child process is running in a *pseudo terminal* ([pty](https://docs.python.org/3/library/pty.html)) which is then connected to the Terminal.\n",
    "- **``kill``** (action): Kills the subprocess if it is running.\n",
    "\n",
    "___"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "terminal = pn.widgets.Terminal(\n",
    "    \"Welcome to the Panel Terminal!\\nI'm based on xterm.js\\n\\n\",\n",
    "    options={\"cursorBlink\": True},\n",
    "    height=300, sizing_mode='stretch_width'\n",
    ")\n",
    "\n",
    "terminal"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Writing strings to the terminal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "terminal.write(\"This is written directly to the terminal.\\n\")\n",
    "terminal.write(\"Danish Characters: æøåÆØÅ\\n\")\n",
    "terminal.write(\"Emoji: Python 🐍  Panel ❤️ 😊 \\n\")\n",
    "terminal.write(\"Links: https://panel.holoviz.org\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Writing stdout to the terminal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sys.stdout = terminal\n",
    "print(\"This print statement is redirected from stdout to the Panel Terminal\")\n",
    "\n",
    "sys.stdout = sys.__stdout__\n",
    "print(\"This print statement is again redirected to the server console\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Logging to the terminal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "logger = logging.getLogger(\"terminal\")\n",
    "logger.setLevel(logging.DEBUG)\n",
    "\n",
    "stream_handler = logging.StreamHandler(terminal) # NOTE THIS\n",
    "stream_handler.terminator = \"  \\n\"\n",
    "formatter = logging.Formatter(\"%(asctime)s [%(levelname)s]: %(message)s\")\n",
    "\n",
    "stream_handler.setFormatter(formatter)\n",
    "stream_handler.setLevel(logging.DEBUG)\n",
    "logger.addHandler(stream_handler)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "logger.info(\"Hello Info Logger\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Streaming to the terminal\n",
    "\n",
    "We only do this to a reduced amount as you can reach a general rate limit in the notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in range(0, 50):\n",
    "    logger.info(uuid.uuid4()) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Run SubProcess and direct output to Terminal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "terminal.subprocess.run(\"ls\", \"-l\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let us review the output so far since a static rendering of this page will not dynamically update the contents of the terminal displayed above:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "terminal"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Clear the Terminal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "terminal.clear()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Run an Interactive Process in the Terminal\n",
    "\n",
    "You can run interactive processes like `bash`, `python`, `ipython` or similar."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "subprocess_terminal = pn.widgets.Terminal(\n",
    "    options={\"cursorBlink\": True},\n",
    "    height=300, sizing_mode='stretch_width'\n",
    ")\n",
    "\n",
    "run_python = pn.widgets.Button(name=\"Run Python\", button_type=\"success\")\n",
    "run_python.on_click(\n",
    "    lambda x: subprocess_terminal.subprocess.run(\"python\")\n",
    ")\n",
    "\n",
    "kill = pn.widgets.Button(name=\"Kill Python\", button_type=\"danger\")\n",
    "kill.on_click(\n",
    "    lambda x: subprocess_terminal.subprocess.kill()\n",
    ")\n",
    "\n",
    "pn.Column(\n",
    "    pn.Row(run_python, kill, subprocess_terminal.subprocess.param.running),\n",
    "    subprocess_terminal,\n",
    "    sizing_mode='stretch_both',\n",
    "    min_height=500\n",
    ")"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
